/**
   \file
   Definitions for GDB integration
*/

/**
   \class GDB
  Interface to debugger  
   
  gdb related options for the simulator
     \arg \c -gdb_wait wait at startup until gdb attaches
     \arg \c --gdb_port: specify a specific port for connection

  Example
  \verbatim
    $ ./test_sieve --- --system scc -gdb_wait --gdb_port 12345
  \endverbatim

  You don't need to specify anything to use gdb.
  If you don't say  -gdb_wait, simulation just proceeds and you can still attach
  If you don't say --gdb_port, you have to lookup the port number in log.sim

  To connect gdb to simulator on a specified port:
  \verbatim
  $ gdb
  (gdb) symbol-file test_sieve
  (gdb) target remote :<port>
  \endverbatim

  You can add "monitor" commands to extend what the simulator can return to gdb.
  For example
  \verbatim
  (gdb) monitor instr_count
  Instruction count for cpu 0: 0
  (gdb) 
  \endverbatim
*/

#ifndef GDB_H
#define GDB_H

#include <setjmp.h>

#include "util/HashSet.h"
#include "util/Logger.h"

// FIXME(mbarrien): This hash function was disabled since the new unordered_set
// implementation already has a hash function for unsigned 64-bit numbers.
// However, unclear whether any part of the code relies on this specific hash
// implementation
/*
namespace std
{
  namespace tr1
  {
    template<> struct hash<VA>
    {
        size_t operator()( const VA& x ) const
        {
            return ADDR_UINT32(x);
        }
    };
  }
}
*/

typedef enum GDB_run_result {
   GDB_CONTINUE     = -2,
   GDB_NEXTI_ANYCPU = -1,
} GDB_run_result;

    
/** 
    jmp_buf for recovering from gdb induced segmentation fauly
*/
extern jmp_buf gdbSIGSEGVJumpBuffer;

class GDB 
{
 public:
    NEW_DELETE_OPERATORS_H(GDB,NoSuper);
    GDB ();
    ~GDB ();

    /**
       Print via debugger interface. 
       
       Whenever the debugger is connected we can use this to send
       output to disable on the host's terminal. Useful for return
       results from monitor commands.
    */
    int GDBPrint (const char *fmt, ...);

    /** 
        Poll checks to see if there is any input from gdb that needs
        servicing. If so, mark our state so that Processor will stop.
        
        If 'blocking' is true, we will wait until there is input,
        otherwise poll returns immediately so simulation can continue.
    */
    void poll (bool blocking);

    /** 
        Called by defaultSigSegvHandler to check if we are currently
        accept interactive commands from a gdb user that caused a
        SIGSEGV.
     */
    bool getInteractive () {
        return interactive;
    }

    /**
       Called by every processor on every cycle to see if its time to stop
    */
    void debug (int cpunum, VA pc, bool firstCycle) {

        /* 
           Only two reasons to stop
           1.) If we are interactive and its the right time and processor
           2.) We have hit a breakpoint on any processor
        */
        // Case 1.
        if (interactive) {
            // we only stop if this is the first cycle for this pc,
            // unless we are in the special break every cycle mode
            if (!(firstCycle || breakNextCycle)) {
                return;
            }
            // we only stop if we have a matching CPU
            if (!(breakNextInstructionCPU == cpunum || 
                  breakNextInstructionCPU == -1)) {
                return;
            }
        }
        // Case 2.
        else if (breakpoints.empty() || !breakpointHit(pc)) {
            return;
        }

        // don't want to automatically stop next cycle unless requested again!
        breakNextCycle = false;

        // We are logically stopped...
        // run handles talking to the host
        GDB_run_result res = run(cpunum);
        // How do we proceed?
        switch (res) {
            // continue by running until event
        case GDB_CONTINUE:
            interactive = false;
            breakNextInstructionCPU = -1;
            break;
            // continue by stepping
        case GDB_NEXTI_ANYCPU:
        default:
            interactive = true;
            breakNextInstructionCPU = res;
            break;
        }
    }
    
 private:
    /**
       Run performs interaction with the host gdb. It is called
       whenever there is input detected from the host.

       Return values from run:
       GDB_CONTINUE      run free
       GDB_NEXTI_ANYCPU  execute one instruction, stop before next instruction on any cpu is executed
       val >= 0          execute one instruction, stop before next inst on cpu <returnval> is executed
   
       The 'cpunum' argument indicates which cpu to focus the debugger on
       when it starts up.
    */
    GDB_run_result run (int cpunum);

    /**
       For debugging GDB remote target protocol
     */
    Logger* gdbLog;

    /** 
        interactive is true when the gdb host is interacting with the
        target simulator. it is not true, just because we have a
        connection, but only if the user is logically at a "(gdb)"
        prompt.
     */
    bool interactive;

    /** 
        pendingIO is true when we poll has seen I/O that needs to be consumed
     */
    bool pendingIO;

    /** 
        breakNextInstructionCPU is the CPU to stop at the start of the next
        logical instruction. -1 implies any CPU, and is typically used
        when gdb first attaches or sends us a Control-C.
     */
    int breakNextInstructionCPU;

    /** 
        breakNextCycle means the simulator should stop on the next
        cycle of the next processor.
     */
    bool breakNextCycle;

    /**
       listenFD is the file descriptor that the simulator uses to
       listen for new host gdb connections.
     */
    int listenFD;

    /**
       hostFD is the file descriptor that the simulator uses to
       talk to an active gdb connection.
     */
    int hostFD;

    /**
       Return the file decriptor to use for polling.  Someone needs to
       poll the gdb connection if it is open in case the user types
       ^C.  This is currently done every CHECK_GDB_INTERVAL cycles by
       the Simulator::run. The fd could be for the listening socket if
       there is no current connection or it could be the active file
       descriptor if we already have a connection.

       returns fd number for input
    */
    int pollfd ();  

    /**
       readchar returns next char from host GDB or -1 if error.  
    */
    int readchar ();

    /**
       putpkt sends a packet to the remote machine, with error checking.
       
       The data of the packet is in 'buf'.  Returns >= 0 on success,
       -1 otherwise.
    */
    int putpkt (char *buf);

    /**
       getpkt reads a packet from the remote machine, with error
       checking, and store it in 'buf'.  Returns length of packet, or
       negative if error.
    */
    int getpkt (char *buf);

    /** 
        Set of PCs that are active breakpoints.
     */
    HashSet<VA> breakpoints;

    /**
       breakpointHit -- return TRUE if PC matches any breakpoint address
    */
    bool breakpointHit (VA va);

    /**
       addBreakpoint -- add entry to table; ignore if would overflow
    */
    void addBreakpoint (VA va);

    /**
       deleteBreakpoint -- delete an entry; va == -1 means delete entire table
                           ignore if asked to delete non-existent entry
    */
    void deleteBreakpoint (VA va);
};

#endif /* GDB_H */
